1 <?php
2 if(!defined('datalist_db_encoding')) define('datalist_db_encoding', 'iso-8859-1');
3 if(function_exists('date_default_timezone_set')) @date_default_timezone_set('America/New_York');
4
5 /* force caching */
6 $last_modified = filemtime(__FILE__);
7 $last_modified_gmt = gmdate('D, d M Y H:i:s', $last_modified) . ' GMT';
8 $headers = (function_exists('getallheaders') ? getallheaders() : $_SERVER);
9 if(isset($headers['If-Modified-Since']) && (strtotime($headers['If-Modified-Since']) == $last_modified)){
10 @header("Last-Modified: {$last_modified_gmt}", true, 304);
11 @header("Cache-Control: public, max-age=240", true);
12 exit;
13 }
14
15 @header("Last-Modified: {$last_modified_gmt}", true, 200);
16 @header("Cache-Control: public, max-age=240", true);
17 @header('Content-Type: text/javascript; charset=' . datalist_db_encoding);
18 $currDir = dirname(__FILE__);
19 include("{$currDir}/defaultLang.php");
20 include("{$currDir}/language.php");
21 ?>
22 var AppGini = AppGini || {};
23 AppGini.ajaxCache = function(){
24 var _tests = [];
25
26 /*
27 An array of functions that receive a parameterless url and a parameters object,
28 makes a test,
29 and if test passes, executes something and/or
30 returns a non-false value if test passes,
31 or false if test failed (useful to tell if tests should continue or not)
32 */
33 var addCheck = function(check){ //
34 if(typeof(check) == 'function'){
35 _tests.push(check);
36 }
37 };
38
39 var _jqAjaxData = function(opt){ //
40 var opt = opt || {};
41 var url = opt.url || '';
42 var data = opt.data || {};
43
44 var params = url.match(/\?(.*)$/);
45 var param = (params !== null ? params[1] : '');
46
47 var sPageURL = decodeURIComponent(param),
48 sURLVariables = sPageURL.split('&'),
49 sParameter,
50 i;
51
52 for(i = 0; i < sURLVariables.length; i++){
53 sParameter = sURLVariables[i].split('=');
54 if(sParameter[0] == '') continue;
55 data[sParameter[0]] = sParameter[1] || '';
56 }
57
58 return data;
59 };
60
61 var start = function(){ //
62 if(!_tests.length) return; // no need to monitor ajax requests since no checks were defined
63 var reqTests = _tests;
64 $j.ajaxPrefilter(function(options, originalOptions, jqXHR){
65 var success = originalOptions.success || $j.noop,
66 data = _jqAjaxData(originalOptions),
67 oUrl = originalOptions.url || '',
68 url = oUrl.match(/\?/) ? oUrl.match(/(.*)\?/)[1] : oUrl;
69
70 options.beforeSend = function(){ //
71 var req, cached = false, resp;
72
73 for(var i = 0; i < reqTests.length; i++){
74 resp = reqTests[i](url, data);
75 if(resp === false) continue;
76
77 success(resp);
78 return false;
79 }
80
81 return true;
82 }
83 });
84 };
85
86 return {
87 addCheck: addCheck,
88 start: start
89 };
90 };
91
92 /* initials and fixes */
93 jQuery(function(){
94 AppGini.count_ajaxes_blocking_saving = 0;
95
96 /* add ":truncated" pseudo-class to detect elements with clipped text */
97 $j.expr[':'].truncated = function(obj){
98 var $this = $j(obj);
99 var $c = $this
100 .clone()
101 .css({ display: 'inline', width: 'auto', visibility: 'hidden', 'padding-right': 0 })
102 .css({ 'font-size': $this.css('font-size') })
103 .appendTo('body');
104
105 var e_width = $this.outerWidth();
106 var c_width = $c.outerWidth();
107 $c.remove();
108
109 return ( c_width > e_width );
110 };
111
112 $j(window).resize(function(){
113 var window_width = $j(window).width();
114 var max_width = $j('body').width() * 0.5;
115
116 if($j('fieldset .col-xs-11').length) max_width = $j('fieldset .col-xs-11').width() - select2_max_width_decrement();
117 $j('.select2-container:not(.option_list)').css({ 'max-width' : max_width + 'px', 'width': '100%' });
118 fix_table_responsive_width();
119
120 var full_img_factor = 0.9; /* xs */
121 if(window_width >= 992) full_img_factor = 0.6; /* md, lg */
122 else if(window_width >= 768) full_img_factor = 0.9; /* sm */
123
124 $j('.detail_view .img-responsive').css({'max-width' : parseInt($j('.detail_view').width() * full_img_factor) + 'px'});
125
126 /* remove labels from truncated buttons, leaving only glyphicons */
127 $j('.btn:truncated').each(function(){
128 // hide text
129 var label = $j(this).html();
130 var mlabel = label.replace(/.*(<i.*?><\/i>).*/, '$1');
131 $j(this).html(mlabel);
132 });
133 });
134
135 setTimeout(function(){ $j(window).resize(); }, 1000);
136 setTimeout(function(){ $j(window).resize(); }, 3000);
137
138 /* don't allow saving detail view when there's an ajax request to a url that matches the following */
139 var ajax_blockers = new RegExp(/(ajax_combo\.php|_autofill\.php|ajax_check_unique\.php)/);
140 $j(document).ajaxSend(function(e, r, s){
141 if(s.url.match(ajax_blockers)){
142 AppGini.count_ajaxes_blocking_saving++;
143 $j('#update, #insert').prop('disabled', true);
144 }
145 });
146 $j(document).ajaxComplete(function(e, r, s){
147 if(s.url.match(ajax_blockers)){
148 AppGini.count_ajaxes_blocking_saving = Math.max(AppGini.count_ajaxes_blocking_saving - 1, 0);
149 if(AppGini.count_ajaxes_blocking_saving <= 0)
150 $j('#update, #insert').prop('disabled', false);
151 }
152 });
153
154 /* don't allow responsive images to initially exceed the smaller of their actual dimensions, or .6 container width */
155 jQuery('.detail_view .img-responsive').each(function(){
156 var pic_real_width, pic_real_height;
157 var img = jQuery(this);
158 jQuery('<img/>') // Make in memory copy of image to avoid css issues
159 .attr('src', img.attr('src'))
160 .load(function() {
161 pic_real_width = this.width;
162 pic_real_height = this.height;
163
164 if(pic_real_width > $j('.detail_view').width() * .6) pic_real_width = $j('.detail_view').width() * .6;
165 img.css({ "max-width": pic_real_width });
166 });
167 });
168
169 jQuery('.table-responsive .img-responsive').each(function(){
170 var pic_real_width, pic_real_height;
171 var img = jQuery(this);
172 jQuery('<img/>') // Make in memory copy of image to avoid css issues
173 .attr('src', img.attr('src'))
174 .load(function() {
175 pic_real_width = this.width;
176 pic_real_height = this.height;
177
178 if(pic_real_width > $j('.table-responsive').width() * .6) pic_real_width = $j('.table-responsive').width() * .6;
179 img.css({ "max-width": pic_real_width });
180 });
181 });
182
183 /* toggle TV action buttons based on selected records */
184 jQuery('.record_selector').click(function(){
185 var id = jQuery(this).val();
186 var checked = jQuery(this).prop('checked');
187 update_action_buttons();
188 });
189
190 /* select/deselect all records in TV */
191 jQuery('#select_all_records').click(function(){
192 jQuery('.record_selector').prop('checked', jQuery(this).prop('checked'));
193 update_action_buttons();
194 });
195
196 /* fix behavior of select2 in bootstrap modal. See: https://github.com/ivaynberg/select2/issues/1436 */
197 jQuery.fn.modal.Constructor.prototype.enforceFocus = function(){ /**/ };
198
199 /* remove empty navbar menus */
200 $j('nav li.dropdown').each(function(){
201 var num_items = $j(this).children('.dropdown-menu').children('li').length;
202 if(!num_items) $j(this).remove();
203 })
204
205 update_action_buttons();
206
207 /* remove empty images and links from TV, TVP */
208 $j('.table a[href="<?php echo $Translation['ImageFolder']; ?>"], .table img[src="<?php echo $Translation['ImageFolder']; ?>"]').remove();
209
210 /* remove empty email links from TV, TVP */
211 $j('a[href="mailto:"]').remove();
212 });
213
214 /* show/hide TV action buttons based on whether records are selected or not */
215 function update_action_buttons(){
216 if(jQuery('.record_selector:checked').length){
217 jQuery('.selected_records').removeClass('hidden');
218 jQuery('#select_all_records')
219 .prop('checked', (jQuery('.record_selector:checked').length == jQuery('.record_selector').length));
220 }else{
221 jQuery('.selected_records').addClass('hidden');
222 }
223 }
224
225 /* fix table-responsive behavior on Chrome */
226 function fix_table_responsive_width(){
227 var resp_width = jQuery('div.table-responsive').width();
228 var table_width;
229
230 if(resp_width){
231 jQuery('div.table-responsive table').width('100%');
232 table_width = jQuery('div.table-responsive table').width();
233 resp_width = jQuery('div.table-responsive').width();
234 if(resp_width == table_width){
235 jQuery('div.table-responsive table').width(resp_width - 1);
236 }
237 }
238 }
239
240 function patients_validateData(){
241 $j('.has-error').removeClass('has-error');
242 if($j('#last_name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Last name", close: function(){ $j('[name=last_name]').focus(); $j('[name=last_name]').parents('.form-group').addClass('has-error'); } }); return false; };
243 if($j('#first_name').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> First name", close: function(){ $j('[name=first_name]').focus(); $j('[name=first_name]').parents('.form-group').addClass('has-error'); } }); return false; };
244 if($j('#gender').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Gender", close: function(){ $j('[name=gender]').focus(); $j('[name=gender]').parents('.form-group').addClass('has-error'); } }); return false; };
245 if($j('#sexual_orientation').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Sexual orientation", close: function(){ $j('[name=sexual_orientation]').focus(); $j('[name=sexual_orientation]').parents('.form-group').addClass('has-error'); } }); return false; };
246 if($j('#tobacco_usage').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Tobacco usage", close: function(){ $j('[name=tobacco_usage]').focus(); $j('[name=tobacco_usage]').parents('.form-group').addClass('has-error'); } }); return false; };
247 if($j('#alcohol_intake').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Alcohol Intake", close: function(){ $j('[name=alcohol_intake]').focus(); $j('[name=alcohol_intake]').parents('.form-group').addClass('has-error'); } }); return false; };
248 if($j('#history').val() == ''){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> History", close: function(){ $j('[name=history]').focus(); $j('[name=history]').parents('.form-group').addClass('has-error'); } }); return false; };
249 return true;
250 }
251 function disease_symptoms_validateData(){
252 $j('.has-error').removeClass('has-error');
253 return true;
254 }
255 function medical_records_validateData(){
256 $j('.has-error').removeClass('has-error');
257 return true;
258 }
259 function events_validateData(){
260 $j('.has-error').removeClass('has-error');
261 if(!$j('[name=status]:checked').length){ modal_window({ message: '<div class="alert alert-danger"><?php echo addslashes($Translation['field not null']); ?></div>', title: "<?php echo addslashes($Translation['error:']); ?> Status", close: function(){ $j('[name=status]').focus(); $j('[name=status]').parents('.form-group').addClass('has-error'); } }); return false; };
262 return true;
263 }
264
265 function post(url, params, update, disable, loading, success_callback){
266 $j.ajax({
267 url: url,
268 type: 'POST',
269 data: params,
270 beforeSend: function() {
271 if($j('#' + disable).length) $j('#' + disable).prop('disabled', true);
272 if($j('#' + loading).length && update != loading) $j('#' + loading).html('<div style="direction: ltr;"><img src="loading.gif"> <?php echo addslashes($Translation['Loading ...']); ?></div>');
273 },
274 success: function(resp) {
275 if($j('#' + update).length) $j('#' + update).html(resp);
276 if(success_callback != undefined) success_callback();
277 },
278 complete: function() {
279 if($j('#' + disable).length) $j('#' + disable).prop('disabled', false);
280 if($j('#' + loading).length && loading != update) $j('#' + loading).html('');
281 }
282 });
283 }
284
285 function post2(url, params, notify, disable, loading, redirectOnSuccess){
286 new Ajax.Request(
287 url, {
288 method: 'post',
289 parameters: params,
290 onCreate: function() {
291 if($(disable) != undefined) $(disable).disabled=true;
292 if($(loading) != undefined) $(loading).show();
293 },
294 onSuccess: function(resp) {
295 /* show notification containing returned text */
296 if($(notify) != undefined) $(notify).removeClassName('Error').appear().update(resp.responseText);
297
298 /* in case no errors returned, */
299 if(!resp.responseText.match(/<?php echo $Translation['error:']; ?>/)){
300 /* redirect to provided url */
301 if(redirectOnSuccess != undefined){
302 window.location=redirectOnSuccess;
303
304 /* or hide notification after a few seconds if no url is provided */
305 }else{
306 if($(notify) != undefined) window.setTimeout(function(){ $(notify).fade(); }, 15000);
307 }
308
309 /* in case of error, apply error class */
310 }else{
311 $(notify).addClassName('Error');
312 }
313 },
314 onComplete: function() {
315 if($(disable) != undefined) $(disable).disabled=false;
316 if($(loading) != undefined) $(loading).hide();
317 }
318 }
319 );
320 }
321 function passwordStrength(password, username){
322 // score calculation (out of 10)
323 var score = 0;
324 re = new RegExp(username, 'i');
325 if(username.length && password.match(re)) score -= 5;
326 if(password.length < 6) score -= 3;
327 else if(password.length > 8) score += 5;
328 else score += 3;
329 if(password.match(/(.*[0-9].*[0-9].*[0-9])/)) score += 3;
330 if(password.match(/(.*[!,@,#,$,%,^,&,*,?,_,~].*[!,@,#,$,%,^,&,*,?,_,~])/)) score += 5;
331 if(password.match(/([a-z].*[A-Z])|([A-Z].*[a-z])/)) score += 2;
332
333 if(score >= 9)
334 return 'strong';
335 else if(score >= 5)
336 return 'good';
337 else
338 return 'weak';
339 }
340 function validateEmail(email) {
341 var re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/;
342 return re.test(email);
343 }
344 function loadScript(jsUrl, cssUrl, callback){
345 // adding the script tag to the head
346 var head = document.getElementsByTagName('head')[0];
347 var script = document.createElement('script');
348 script.type = 'text/javascript';
349 script.src = jsUrl;
350
351 if(cssUrl != ''){
352 var css = document.createElement('link');
353 css.href = cssUrl;
354 css.rel = "stylesheet";
355 css.type = "text/css";
356 head.appendChild(css);
357 }
358
359 // then bind the event to the callback function
360 // there are several events for cross browser compatibility
361 if(script.onreadystatechange != undefined){ script.onreadystatechange = callback; }
362 if(script.onload != undefined){ script.onload = callback; }
363
364 // fire the loading
365 head.appendChild(script);
366 }
367 /**
368 * options object. The following members can be provided:
369 * url: iframe url to load
370 * message: instead of a url to open, you could pass a message. HTML tags allowed.
371 * id: id attribute of modal window
372 * title: optional modal window title
373 * size: 'default', 'full'
374 * close: optional function to execute on closing the modal
375 * footer: optional array of objects describing the buttons to display in the footer.
376 * Each button object can have the following members:
377 * label: string, label of button
378 * bs_class: string, button bootstrap class. Can be 'primary', 'default', 'success', 'warning' or 'danger'
379 * click: function to execute on clicking the button. If the button closes the modal, this
380 * function is executed before the close handler
381 * causes_closing: boolean, default is true.
382 */
383 function modal_window(options){
384 var id = options.id;
385 var url = options.url;
386 var title = options.title;
387 var footer = options.footer;
388 var message = options.message;
389
390 if(typeof(id) == 'undefined') id = random_string(20);
391 if(typeof(footer) == 'undefined') footer = [];
392
393 if(jQuery('#' + id).length){
394 /* modal exists -- remove it first */
395 jQuery('#' + id).remove();
396 }
397
398 /* prepare footer buttons, if any */
399 var footer_buttons = '';
400 for(i = 0; i < footer.length; i++){
401 if(typeof(footer[i].causes_closing) == 'undefined'){ footer[i].causes_closing = true; }
402 if(typeof(footer[i].bs_class) == 'undefined'){ footer[i].bs_class = 'default'; }
403 footer[i].id = id + '_footer_button_' + random_string(10);
404
405 footer_buttons += '<button type="button" class="btn btn-' + footer[i].bs_class + '" ' +
406 (footer[i].causes_closing ? 'data-dismiss="modal" ' : '') +
407 'id="' + footer[i].id + '" ' +
408 '>' + footer[i].label + '</button>';
409 }
410
411 jQuery('body').append(
412 '<div class="modal fade" id="' + id + '" tabindex="-1" role="dialog" aria-labelledby="myModalLabel" aria-hidden="true">' +
413 '<div class="modal-dialog">' +
414 '<div class="modal-content">' +
415 ( title != undefined ?
416 '<div class="modal-header">' +
417 '<button type="button" class="close" data-dismiss="modal" aria-hidden="true">×</button>' +
418 '<h3 class="modal-title" id="myModalLabel">' + title + '</h3>' +
419 '</div>'
420 : ''
421 ) +
422 '<div class="modal-body" style="-webkit-overflow-scrolling:touch !important; overflow-y: auto;">' +
423 ( url != undefined ?
424 '<iframe width="100%" height="100%" sandbox="allow-modals allow-forms allow-scripts allow-same-origin allow-popups" src="' + url + '"></iframe>'
425 : message
426 ) +
427 '</div>' +
428 ( footer != undefined ?
429 '<div class="modal-footer">' + footer_buttons + '</div>'
430 : ''
431 ) +
432 '</div>' +
433 '</div>' +
434 '</div>'
435 );
436
437 for(i = 0; i < footer.length; i++){
438 if(typeof(footer[i].click) == 'function'){
439 jQuery('#' + footer[i].id).click(footer[i].click);
440 }
441 }
442
443 jQuery('#' + id).modal();
444
445 if(typeof(options.close) == 'function'){
446 jQuery('#' + id).on('hidden.bs.modal', options.close);
447 }
448
449 if(typeof(options.size) == 'undefined') options.size = 'default';
450
451 if(options.size == 'full'){
452 jQuery(window).resize(function(){
453 jQuery('#' + id + ' .modal-dialog').width(jQuery(window).width() * 0.95);
454 jQuery('#' + id + ' .modal-body').height(jQuery(window).height() * 0.7);
455 }).trigger('resize');
456 }
457
458 return id;
459 }
460
461 function random_string(string_length){
462 var text = "";
463 var possible = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789";
464
465 for(var i = 0; i < string_length; i++)
466 text += possible.charAt(Math.floor(Math.random() * possible.length));
467
468 return text;
469 }
470
471 function get_selected_records_ids(){
472 return jQuery('.record_selector:checked').map(function(){ return jQuery(this).val() }).get();
473 }
474
475 function print_multiple_dv_tvdv(t, ids){
476 document.myform.NoDV.value=1;
477 document.myform.PrintDV.value=1;
478 document.myform.SelectedID.value = '';
479 document.myform.submit();
480 return true;
481 }
482
483 function print_multiple_dv_sdv(t, ids){
484 document.myform.NoDV.value=1;
485 document.myform.PrintDV.value=1;
486 document.myform.writeAttribute('novalidate', 'novalidate');
487 document.myform.submit();
488 return true;
489 }
490
491 function mass_delete(t, ids){
492 if(ids == undefined) return;
493 if(!ids.length) return;
494
495 var confirm_message = '<div class="alert alert-danger">' +
496 '<i class="glyphicon glyphicon-warning-sign"></i> ' +
497 '<?php echo addslashes($Translation['<n> records will be deleted. Are you sure you want to do this?']); ?>' +
498 '</div>';
499 var confirm_title = '<?php echo addslashes($Translation['Confirm deleting multiple records']); ?>';
500 var label_yes = '<?php echo addslashes($Translation['Yes, delete them!']); ?>';
501 var label_no = '<?php echo addslashes($Translation['No, keep them.']); ?>';
502 var progress = '<?php echo addslashes($Translation['Deleting record <i> of <n>']); ?>';
503 var continue_delete = true;
504
505 // request confirmation of mass delete operation
506 modal_window({
507 message: confirm_message.replace(/\<n\>/, ids.length),
508 title: confirm_title,
509 footer: [ /* shows a 'yes' and a 'no' buttons .. handler for each follows ... */
510 {
511 label: '<i class="glyphicon glyphicon-trash"></i> ' + label_yes,
512 bs_class: 'danger',
513 // on confirming, start delete operations
514 click: function(){
515
516 // show delete progress, allowing user to abort operations by closing the window or clicking cancel
517 var progress_window = modal_window({
518 title: '<?php echo addslashes($Translation['Delete progress']); ?>',
519 message: '' +
520 '<div class="progress">' +
521 '<div class="progress-bar progress-bar-warning" role="progressbar" style="width: 0;"></div>' +
522 '</div>' +
523 '<button type="button" class="btn btn-default details_toggle" onclick="' +
524 'jQuery(this).children(\'.glyphicon\').toggleClass(\'glyphicon-chevron-right glyphicon-chevron-down\'); ' +
525 'jQuery(\'.well.details_list\').toggleClass(\'hidden\');'
526 + '">' +
527 '<i class="glyphicon glyphicon-chevron-right"></i> ' +
528 '<?php echo addslashes($Translation['Show/hide details']); ?>' +
529 '</button>' +
530 '<div class="well well-sm details_list hidden"><ol></ol></div>',
531 close: function(){
532 // stop deleting further records ...
533 continue_delete = false;
534 },
535 footer: [
536 {
537 label: '<i class="glyphicon glyphicon-remove"></i> <?php echo addslashes($Translation['Cancel']); ?>',
538 bs_class: 'warning'
539 }
540 ]
541 });
542
543 // begin deleting records, one by one
544 progress = progress.replace(/\<n\>/, ids.length);
545 var delete_record = function(itrn){
546 if(!continue_delete) return;
547 jQuery.ajax(t + '_view.php', {
548 type: 'POST',
549 data: { delete_x: 1, SelectedID: ids[itrn] },
550 success: function(resp){
551 if(resp == 'OK'){
552 jQuery(".well.details_list ol").append('<li class="text-success"><?php echo addslashes($Translation['The record has been deleted successfully']); ?></li>');
553 jQuery('#record_selector_' + ids[itrn]).prop('checked', false).parent().parent().fadeOut(1500);
554 jQuery('#select_all_records').prop('checked', false);
555 }else{
556 jQuery(".well.details_list ol").append('<li class="text-danger">' + resp + '</li>');
557 }
558 },
559 error: function(){
560 jQuery(".well.details_list ol").append('<li class="text-warning"><?php echo addslashes($Translation['Connection error']); ?></li>');
561 },
562 complete: function(){
563 jQuery('#' + progress_window + ' .progress-bar').attr('style', 'width: ' + (Math.round((itrn + 1) / ids.length * 100)) + '%;').html(progress.replace(/\<i\>/, (itrn + 1)));
564 if(itrn < (ids.length - 1)){
565 delete_record(itrn + 1);
566 }else{
567 if(jQuery('.well.details_list li.text-danger, .well.details_list li.text-warning').length){
568 jQuery('button.details_toggle').removeClass('btn-default').addClass('btn-warning').click();
569 jQuery('.btn-warning[id^=' + progress_window + '_footer_button_]')
570 .toggleClass('btn-warning btn-default')
571 .html('<?php echo addslashes($Translation['ok']); ?>');
572 }else{
573 setTimeout(function(){ jQuery('#' + progress_window).modal('hide'); }, 500);
574 }
575 }
576 }
577 });
578 }
579
580 delete_record(0);
581 }
582 },
583 {
584 label: '<i class="glyphicon glyphicon-ok"></i> ' + label_no,
585 bs_class: 'success'
586 }
587 ]
588 });
589 }
590
591 function mass_change_owner(t, ids){
592 if(ids == undefined) return;
593 if(!ids.length) return;
594
595 var update_form = '<?php echo addslashes($Translation['Change owner of <n> selected records to']); ?> ' +
596 '<span id="new_owner_for_selected_records"></span><input type="hidden" name="new_owner_for_selected_records" value="">';
597 var confirm_title = '<?php echo addslashes($Translation['Change owner']); ?>';
598 var label_yes = '<?php echo addslashes($Translation['Continue']); ?>';
599 var label_no = '<?php echo addslashes($Translation['Cancel']); ?>';
600 var progress = '<?php echo addslashes($Translation['Updating record <i> of <n>']); ?>';
601 var continue_updating = true;
602
603 // request confirmation of mass update operation
604 modal_window({
605 message: update_form.replace(/\<n\>/, ids.length),
606 title: confirm_title,
607 footer: [ /* shows a 'continue' and a 'cancel' buttons .. handler for each follows ... */
608 {
609 label: '<i class="glyphicon glyphicon-ok"></i> ' + label_yes,
610 bs_class: 'success',
611 // on confirming, start update operations
612 click: function(){
613 var memberID = jQuery('input[name=new_owner_for_selected_records]').eq(0).val();
614 if(!memberID.length) return;
615
616 // show update progress, allowing user to abort operations by closing the window or clicking cancel
617 var progress_window = modal_window({
618 title: '<?php echo addslashes($Translation['Update progress']); ?>',
619 message: '' +
620 '<div class="progress">' +
621 '<div class="progress-bar progress-bar-success" role="progressbar" style="width: 0;"></div>' +
622 '</div>' +
623 '<button type="button" class="btn btn-default details_toggle" onclick="' +
624 'jQuery(this).children(\'.glyphicon\').toggleClass(\'glyphicon-chevron-right glyphicon-chevron-down\'); ' +
625 'jQuery(\'.well.details_list\').toggleClass(\'hidden\');'
626 + '">' +
627 '<i class="glyphicon glyphicon-chevron-right"></i> ' +
628 '<?php echo addslashes($Translation['Show/hide details']); ?>' +
629 '</button>' +
630 '<div class="well well-sm details_list hidden"><ol></ol></div>',
631 close: function(){
632 // stop updating further records ...
633 continue_updating = false;
634 },
635 footer: [
636 {
637 label: '<i class="glyphicon glyphicon-remove"></i> <?php echo addslashes($Translation['Cancel']); ?>',
638 bs_class: 'warning'
639 }
640 ]
641 });
642
643 // begin updating records, one by one
644 progress = progress.replace(/\<n\>/, ids.length);
645 var update_record = function(itrn){
646 if(!continue_updating) return;
647 jQuery.ajax('admin/pageEditOwnership.php', {
648 type: 'POST',
649 data: {
650 pkValue: ids[itrn],
651 t: t,
652 memberID: memberID,
653 saveChanges: 'Save changes'
654 },
655 success: function(resp){
656 if(resp == 'OK'){
657 jQuery(".well.details_list ol").append('<li class="text-success"><?php echo addslashes($Translation['record updated']); ?></li>');
658 jQuery('#record_selector_' + ids[itrn]).prop('checked', false);
659 jQuery('#select_all_records').prop('checked', false);
660 }else{
661 jQuery(".well.details_list ol").append('<li class="text-danger">' + resp + '</li>');
662 }
663 },
664 error: function(){
665 jQuery(".well.details_list ol").append('<li class="text-warning"><?php echo addslashes($Translation['Connection error']); ?></li>');
666 },
667 complete: function(){
668 jQuery('#' + progress_window + ' .progress-bar').attr('style', 'width: ' + (Math.round((itrn + 1) / ids.length * 100)) + '%;').html(progress.replace(/\<i\>/, (itrn + 1)));
669 if(itrn < (ids.length - 1)){
670 update_record(itrn + 1);
671 }else{
672 if(jQuery('.well.details_list li.text-danger, .well.details_list li.text-warning').length){
673 jQuery('button.details_toggle').removeClass('btn-default').addClass('btn-warning').click();
674 jQuery('.btn-warning[id^=' + progress_window + '_footer_button_]')
675 .toggleClass('btn-warning btn-default')
676 .html('<?php echo addslashes($Translation['ok']); ?>');
677 }else{
678 jQuery('button.btn-warning[id^=' + progress_window + '_footer_button_]')
679 .toggleClass('btn-warning btn-success')
680 .html('<i class="glyphicon glyphicon-ok"></i> <?php echo addslashes($Translation['ok']); ?>');
681 }
682 }
683 }
684 });
685 }
686
687 update_record(0);
688 }
689 },
690 {
691 label: '<i class="glyphicon glyphicon-remove"></i> ' + label_no,
692 bs_class: 'warning'
693 }
694 ]
695 });
696
697 /* show drop down of users */
698 var populate_new_owner_dropdown = function(){
699
700 jQuery('[id=new_owner_for_selected_records]').select2({
701 width: '100%',
702 formatNoMatches: function(term){ return '<?php echo addslashes($Translation['No matches found!']); ?>'; },
703 minimumResultsForSearch: 10,
704 loadMorePadding: 200,
705 escapeMarkup: function(m){ return m; },
706 ajax: {
707 url: 'admin/getUsers.php',
708 dataType: 'json',
709 cache: true,
710 data: function(term, page){ return { s: term, p: page, t: t }; },
711 results: function(resp, page){ return resp; }
712 }
713 }).on('change', function(e){
714 jQuery('[name="new_owner_for_selected_records"]').val(e.added.id);
715 });
716
717 }
718
719 populate_new_owner_dropdown();
720 }
721
722 function add_more_actions_link(){
723 window.open('https://bigprof.com/appgini/help/advanced-topics/hooks/multiple-record-batch-actions?r=appgini-action-menu');
724 }
725
726 /* detect current screen size (xs, sm, md or lg) */
727 function screen_size(sz){
728 if(!$j('.device-xs').length){
729 $j('body').append(
730 '<div class="device-xs visible-xs"></div>' +
731 '<div class="device-sm visible-sm"></div>' +
732 '<div class="device-md visible-md"></div>' +
733 '<div class="device-lg visible-lg"></div>'
734 );
735 }
736 return $j('.device-' + sz).is(':visible');
737 }
738
739 /* enable floating of action buttons in DV so they are visible on vertical scrolling */
740 function enable_dvab_floating(){
741 /* already run? */
742 if(window.enable_dvab_floating_run != undefined) return;
743
744 /* scroll action buttons of DV on scrolling DV */
745 $j(window).scroll(function(){
746 if(!screen_size('md') && !screen_size('lg')) return;
747 if(!$j('.detail_view').length) return;
748
749 /* get vscroll amount, DV form height, button toolbar height and position */
750 var vscroll = $j(window).scrollTop();
751 var dv_height = $j('[id$="_dv_form"]').eq(0).height();
752 var bt_height = $j('.detail_view .btn-toolbar').height();
753 var form_top = $j('.detail_view .form-group').eq(0).offset().top;
754 var bt_top_max = dv_height - bt_height - 10;
755
756 if(vscroll > form_top){
757 var tm = parseInt(vscroll - form_top) + 60;
758 if(tm > bt_top_max) tm = bt_top_max;
759
760 $j('.detail_view .btn-toolbar').css({ 'margin-top': tm + 'px' });
761 }else{
762 $j('.detail_view .btn-toolbar').css({ 'margin-top': 0 });
763 }
764 });
765 window.enable_dvab_floating_run = true;
766 }
767
768 /* check if a given field's value is unique and reflect this in the DV form */
769 function enforce_uniqueness(table, field){
770 $j('#' + field).on('change', function(){
771 /* check uniqueness of field */
772 var data = {
773 t: table,
774 f: field,
775 value: $j('#' + field).val()
776 };
777
778 if($j('[name=SelectedID]').val().length) data.id = $j('[name=SelectedID]').val();
779
780 $j.ajax({
781 url: 'ajax_check_unique.php',
782 data: data,
783 complete: function(resp){
784 if(resp.responseJSON.result == 'ok'){
785 $j('#' + field + '-uniqueness-note').hide();
786 $j('#' + field).parents('.form-group').removeClass('has-error');
787 }else{
788 $j('#' + field + '-uniqueness-note').show();
789 $j('#' + field).parents('.form-group').addClass('has-error');
790 $j('#' + field).focus();
791 setTimeout(function(){ $j('#update, #insert').prop('disabled', true); }, 500);
792 }
793 }
794 })
795 });
796 }
797
798 /* persist expanded/collapsed chidren in DVP */
799 function persist_expanded_child(id){
800 var expand_these = Cookies.getJSON('online_clinic_management_system.dvp_expand');
801 if(expand_these == undefined) expand_these = [];
802
803 if($j('[id=' + id + ']').hasClass('active')){
804 if(expand_these.indexOf(id) < 0){
805 // expanded button and not persisting in cookie? save it!
806 expand_these.push(id);
807 Cookies.set('online_clinic_management_system.dvp_expand', expand_these, { expires: 30 });
808 }
809 }else{
810 if(expand_these.indexOf(id) >= 0){
811 // collapsed button and persisting in cookie? remove it!
812 expand_these.splice(expand_these.indexOf(id), 1);
813 Cookies.set('online_clinic_management_system.dvp_expand', expand_these, { expires: 30 });
814 }
815 }
816 }
817
818 /* apply expanded/collapsed status to children in DVP */
819 function apply_persisting_children(){
820 var expand_these = Cookies.getJSON('online_clinic_management_system.dvp_expand');
821 if(expand_these == undefined) return;
822
823 expand_these.each(function(id){
824 $j('[id=' + id + ']:not(.active)').click();
825 });
826 }
827
828 function select2_max_width_decrement(){
829 return ($j('div.container').eq(0).hasClass('theme-compact') ? 99 : 109);
830 }